﻿// DoExcel.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <objbase.h>
using namespace std;

//https://www.cnblogs.com/justinpanrobot/archive/2009/01/06/1370147.html
HRESULT AutoWrap(int autoType, VARIANT *pvResult, IDispatch *pDisp, LPOLESTR ptName, int cArgs...)
{

	va_list marker;
	va_start(marker, cArgs);

	if (!pDisp) {
		//MessageBox(NULL, "NULL IDispatch passed to AutoWrap()", "Error", 0x10010);
		cout << "NULL IDispatch passed to AutoWrap()" << endl;
		_exit(0);
	}

	// Variables used...
	DISPPARAMS dp = { NULL, NULL, 0, 0 };
	DISPID dispidNamed = DISPID_PROPERTYPUT;
	DISPID dispID;
	HRESULT hr;
	char buf[200];
	char szName[200];

	// Convert down to ANSI
	WideCharToMultiByte(CP_ACP, 0, ptName, -1, szName, 256, NULL, NULL);

	// Get DISPID for name passed...
	hr = pDisp->GetIDsOfNames(IID_NULL, &ptName, 1, LOCALE_USER_DEFAULT, &dispID);
	if (FAILED(hr)) {
		sprintf_s(buf, "IDispatch::GetIDsOfNames(\"%s\") failed w/err 0x%08lx", szName, hr);
		//MessageBox(NULL, buf, "AutoWrap()", 0x10010);
		_exit(0);
		return hr;
	}

	// Allocate memory for arguments...
	VARIANT *pArgs = new VARIANT[cArgs + 1];
	// Extract arguments...
	for (int i = 0; i < cArgs; i++) {
		pArgs[i] = va_arg(marker, VARIANT);
	}

	// Build DISPPARAMS
	dp.cArgs = cArgs;
	dp.rgvarg = pArgs;

	// Handle special-case for property-puts!
	if (autoType & DISPATCH_PROPERTYPUT) {
		dp.cNamedArgs = 1;
		dp.rgdispidNamedArgs = &dispidNamed;
	}

	// Make the call!
	hr = pDisp->Invoke(dispID, IID_NULL, LOCALE_SYSTEM_DEFAULT, autoType, &dp, pvResult, NULL, NULL);
	if (FAILED(hr)) {
		sprintf_s(buf, "IDispatch::Invoke(\"%s\"=%08lx) failed w/err 0x%08lx", szName, dispID, hr);
		//MessageBox(NULL, buf, "AutoWrap()", 0x10010);
		_exit(0);
		return hr;
	}
	// End variable-argument section...
	va_end(marker);

	delete[] pArgs;

	return hr;
}

int main()
{
	// 初始化COM库
	CoInitialize(NULL);

	// 获得EXCEL的CLSID
	CLSID clsid;
	HRESULT hr = CLSIDFromProgID(L"Excel.Application", &clsid);

	if (FAILED(hr)) {
		//::MessageBox(NULL, "CLSIDFromProgID() 函数调用失败！", "错误", 0x10010);
		cout << "CLSIDFromProgID() 函数调用失败！" << endl;
		return -1;
	}
	else {
		LPOLESTR ClsBuffer = { 0 };
		StringFromCLSID(clsid,&ClsBuffer);
		cout << "获取Excel.Application的CLSID:";
		wcout << ClsBuffer << endl;
	}

	// 创建实例
	IDispatch *pXlApp;
	hr = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch, (void **)&pXlApp);

	if (FAILED(hr)) {
		//::MessageBox(NULL, "请检查是否已经安装EXCEL！", "错误", 0x10010);
		cout << "请检查是否已经安装EXCEL！错误" << endl;
		return -2;
	}
	else {
		LPOLESTR ClsBuffer = { 0 };
		StringFromIID(IID_IDispatch, &ClsBuffer);
		cout << "获取CLS创建IID的值";
		wcout << ClsBuffer << endl;
	}

	// 显示，将Application.Visible属性置1
	VARIANT x;
	x.vt = VT_I4;
	x.lVal = 1;
	//string value = "Visible";
	WCHAR value[64] = L"Visible";
	cout << "设置Excel打开显示状态:true" << endl;
	AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, const_cast<WCHAR*>(L"Visible"), 1, x);

	//不显示打开？
	//AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlApp, const_cast<WCHAR*>(L"Visible"), 1, 0);

	// 获取Workbooks集合
	IDispatch *pXlBooks;
	{
		VARIANT result;
		VariantInit(&result);
		AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, const_cast<WCHAR*>(L"Workbooks"), 0);
		pXlBooks = result.pdispVal;
	}

	// 调用Workbooks.Add()方法，创建一个新的Workbook
	IDispatch *pXlBook;
	{
		VARIANT result;
		VariantInit(&result);
		AutoWrap(DISPATCH_PROPERTYGET, &result, pXlBooks, const_cast<WCHAR*>(L"Add"), 0);
		pXlBook = result.pdispVal;
	}

	//创建数据数组 
	VARIANT arr;
	WCHAR szTmp[256];
	arr.vt = VT_ARRAY | VT_VARIANT;
	SAFEARRAYBOUND sab[2];

	int columnNum = 10;// pFldNames->size();
	int lineNum = 10;// pReturn->size();
	sab[0].lLbound = 1; sab[0].cElements = lineNum + 1;
	sab[1].lLbound = 1; sab[1].cElements = columnNum;
	arr.parray = SafeArrayCreate(VT_VARIANT, 2, sab);

	for (int i = 0; i < lineNum + 1; i++)
	{
		/*RECORDSET *pRecord = NULL;
		if (i != 0)
			pRecord = pReturn->at(i - 1);*/

		for (int j = 0; j < columnNum; j++)
		{
			string strtemp = "test";
			/*if (i == 0)
				strtemp = _com_util::ConvertBSTRToString(_bstr_t(pFldNames->at(j)));
			else if (pReturn != NULL)
			{
				_variant_t var = pRecord->at(j);
				if (var.vt == VT_NULL)
					strtemp = "";
				else
					strtemp = (LPCTSTR)(_bstr_t)var;
			}*/

			VARIANT tmp;
			tmp.vt = VT_BSTR;
			int   nLen = strlen(strtemp.c_str()) + 1;
			MultiByteToWideChar(CP_ACP, 0, strtemp.c_str(), nLen, szTmp, 256);
			tmp.bstrVal = SysAllocString(szTmp);

			// 添加数据到数组中  
			long indices[] = { i + 1,j + 1 };
			SafeArrayPutElement(arr.parray, indices, (void *)&tmp);
		}

	}

	// 从Application.ActiveSheet属性获得Worksheet对象
	IDispatch *pXlSheet;
	{
		VARIANT result;
		VariantInit(&result);
		AutoWrap(DISPATCH_PROPERTYGET, &result, pXlApp, const_cast<WCHAR*>(L"ActiveSheet"), 0);
		pXlSheet = result.pdispVal;
	}

	// 选择一个columnNum*（lineNum+1）大小的Range
	IDispatch *pXlRange;
	{
		VARIANT parm;
		parm.vt = VT_BSTR;

		//CString strset;
		//if (columnNum >= 26) // 因为前26列用A～Z表示，大于26列都是用两个字母表示的，如从27列开始是AA、AB... 
		//	strset.Format(_T("A1:%c%c%d"), columnNum / 26 + 64, columnNum % 26 + 64, lineNum + 1);
		//else
		//	strset.Format(_T("A1:%c%d"), columnNum + 64, lineNum + 1);

		WCHAR wBuf[100]=L"A1:J10";
		//::MultiByteToWideChar(CP_ACP, 0, strset.GetBuffer(), strset.GetLength() + 1, wBuf, 100);
		parm.bstrVal = ::SysAllocString(wBuf);

		VARIANT result;
		VariantInit(&result);
		AutoWrap(DISPATCH_PROPERTYGET, &result, pXlSheet, const_cast<WCHAR*>(L"Range"), 1, parm);
		VariantClear(&parm);

		pXlRange = result.pdispVal;
	}

	// 用我们的数组填充这个Range
	AutoWrap(DISPATCH_PROPERTYPUT, NULL, pXlRange, const_cast<WCHAR*>(L"Value"), 1, arr);


	// 接下来我们该保存文件了，利用Worksheet.SaveAs()方法(我这里忽略了其他所有参数，除了文件名)
	{
		VARIANT filename;
		filename.vt = VT_BSTR;

		int index = 1;// GetFileIndex();
		if (index == 0)
			return -1;

		char pathBuf[MAX_PATH];
		DWORD dw = GetCurrentDirectoryA(MAX_PATH, pathBuf);
		if (dw == 0)return -1;
		/*strcat_s(pathBuf, "http://www.cnblogs.com/justinpanrobot/admin/file://temp/");

		char fileBuf[30] = "";
		sprintf_s(fileBuf, "http://www.cnblogs.com/justinpanrobot/admin/file://dtmp%25d.xls%22,index/);
		strcat_s(pathBuf, fileBuf);*/

		strcat_s(pathBuf, "\\new.xls");
		wcout << "SAVE_PATH:" << pathBuf << endl;
		WCHAR szTmp[256];
		int nlen = strlen(pathBuf) + 1;
		MultiByteToWideChar(CP_ACP, 0, pathBuf, nlen, szTmp, 256);
		filename.bstrVal = SysAllocString(szTmp);
		AutoWrap(DISPATCH_METHOD, NULL, pXlSheet, const_cast<WCHAR*>(L"SaveAs"), 1, filename);
	}


	// 退出，调用Application.Quit()方法
	AutoWrap(DISPATCH_METHOD, NULL, pXlApp, const_cast<WCHAR*>(L"Quit"), 0);

	// 释放所有的接口以及变量
	pXlRange->Release();
	pXlSheet->Release();
	pXlBook->Release();
	pXlBooks->Release();
	pXlApp->Release();
	VariantClear(&arr);

	// 注销COM库
	CoUninitialize();
    std::cout << "Hello World!\n";
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件，或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来，若要再次打开此项目，请转到“文件”>“打开”>“项目”并选择 .sln 文件
